// https://xue.kaikeba.com/interactive-course/12113
// 灌溉
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
#include <sstream>
#include <map>
#include <vector>
#include <set>
#include <unordered_map>
#include <time.h>
#include <stdint.h>
#include <queue>
#include <unordered_set>
#include <stack>

using namespace std;

const int maxn = 1e4 + 10;
const int inf = 0x3f3f3f3f;

int p[maxn];

struct edge {
    int v, u, c; // u和v有连接关系, 代价是c
};

std::vector<edge> e;

bool cmp(const edge &a, const edge &b) {
    return a.c < b.c;
}

// merge - find

int find(int x) {
    if(p[x] != x) {
        return p[x] = find(p[x]);
    }
    return p[x];
}

void merge(int x, int y) {
    int fx = find(x);
    int fy = find(y);
    p[fx] = fy;
    return ;
}


int main() {
    int n, x, ans = 0;
    scanf("%d", &n);

    for(int i = 0; i <= n; i++) {
        p[i] = i;
    }

    // 将邻接矩阵 转换为 邻接表
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= n; j++) {
            scanf("%d", &x); // i->j value : x
            e.push_back((edge){i, j, x});
        }
    }
    // 排序，从小到大
    sort(e.begin(), e.end(), cmp);
    // 选择最小的路径开始加入组中，直到全部加入组中为止，就得到了ans
    for(int i = 0; i < e.size(); i++) {
        int v = e[i].v, u = e[i].u, c = e[i].c;
        // 如果已经在一个组里了，什么都不做
        if(find(v) == find(u)) continue;
        // 否则，加入到同一个组中
        merge(v, u);
        // 将距离加入ans中
        ans += c;
    }

    printf("%d\n", ans);

    return 0;
}

